//
// PepServlet
//
// Copyright (C) 2007 Brian Stevens.
//
//  This file is part of the JavaPepper Library.
//
//  The JavaPepper Library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  The JavaPepper Library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with the JavaPepper Library; if not, write to the Free
//  Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
//  02111-1307 USA.
//

package net.jextra.javapepper.servlet;

import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import net.jextra.javapepper.*;
import org.xml.sax.*;

/**
 * <p>
 * The PepServlet is the entry point servlet for all Pepper web pages. The fundamental function of the servlet is to
 * find an existing {@link PepSession} or create a new one and call its processRequest method.
 * </p>
 * <p>
 * The following is an example of what must be placed in the web.xml file in order to use Pepper.
 * 
 * <pre>
 *   &lt;servlet&gt;
 *       &lt;servlet-name&gt;com.javapepper.core.PepServlet&lt;/servlet-name&gt;
 *       &lt;servlet-class&gt;com.javapepper.core.PepServlet&lt;/servlet-class&gt;
 *       &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
 *       &lt;init-param&gt;
 *           &lt;param-name&gt;application&lt;/param-name&gt;
 *           &lt;param-value&gt;com.javapepper.example.ExampleServer&lt;/param-value&gt;
 *       &lt;/init-param&gt;
 *   &lt;/servlet&gt;
 * 
 *   &lt;servlet-mapping&gt;
 *       &lt;servlet-name&gt;com.javapepper.core.PepServlet&lt;/servlet-name&gt;
 *       &lt;url-pattern&gt;/&lt;/url-pattern&gt;
 *   &lt;/servlet-mapping&gt;
 * </pre>
 **/
public class PepServlet extends HttpServlet
{
    // ============================================================
    // Fields
    // ============================================================

    public static final String INIT_PARAM_APPLICATION = "application";

    private static PepServer server;
    private static PepApplication application;

    // This map keeps track of which thread is being used to process which HttpSession. This
    // is essential in determining the active HttpSession. See {@link #getActiveHttpSession}.
    private static WeakHashMap<Thread, HttpSession> threadMap;

    // This map holds a one-to-one map between an HttpSession and a PepSession.
    // The reason it is weak is so when a HttpSession is destroyed due to inactivity, then the
    // PepSession will be destroyed also.
    private static WeakHashMap<HttpSession, PepSession> pepSessionMap;

    // ============================================================
    // Constructors
    // ============================================================

    static
    {
        threadMap = new WeakHashMap<Thread, HttpSession>();
        pepSessionMap = new WeakHashMap<HttpSession, PepSession>();
    }

    // ============================================================
    // Methods
    // ============================================================

    // ----------
    // public
    // ----------

    public static PepSession getActivePepSession()
        throws FileNotFoundException, SAXException, IOException
    {
        HttpSession httpSession = getActiveHttpSession();
        if ( httpSession == null )
        {
            return null;
        }

        return getPepSession( httpSession );
    }

    /**
     * This uses the active thread to determine the active Http Session.
     */
    public static HttpSession getActiveHttpSession()
    {
        if ( threadMap.get( Thread.currentThread() ) == null )
        {
            return null;
        }

        return threadMap.get( Thread.currentThread() );
    }

    public static PepSession getPepSession( HttpSession httpSession )
        throws FileNotFoundException, SAXException, IOException
    {
        PepSession pepSession = pepSessionMap.get( httpSession );
        if ( pepSession == null )
        {
            //
            // Read configuration file.
            //
            //            File configFile = new File( httpSession.getServletContext().getRealPath( configurationFilename ) );
            //            if ( !configFile.exists() )
            //            {
            //                System.err.println( "JAVAPEPPER ERROR: JavaPepper config file " + configFile.getAbsolutePath() +
            //                    " does not exist." );
            //            }
            //            else
            //            {
            //                PepConfigurationReader configReader = new PepConfigurationReader();
            //                configReader.parse( configFile );
            //
            //                //
            //                // Create instance of PepSession or sub-class and map it to the HttpSession.
            //                //
            //                pepSession = configReader.getPepSession();
            //                pepSession.setHttpSession( httpSession );
            //                pepSessionMap.put( httpSession, pepSession );
            //            }
        }

        return pepSession;
    }

    @Override
    public String getServletInfo()
    {
        return "Pepper";
    }

    @Override
    public void init( ServletConfig config )
        throws ServletException
    {
        System.out.println( "*** INIT" );
        super.init( config );

        try
        {
            String appClassName = config.getInitParameter( INIT_PARAM_APPLICATION );
            application = (PepApplication) Class.forName( appClassName ).newInstance();
            server = new PepServer();

            application.init( server );
        }
        catch ( Exception ex )
        {
            ex.printStackTrace();
            throw new ServletException( ex );
        }
    }

    /**
     * Handles the HTTP <code>DELETE</code> method.
     * 
     * @see #processRequest
     */
    @Override
    public void doDelete( HttpServletRequest request, HttpServletResponse response )
    {
        processRequest( request, response );
    }

    /**
     * Handles the HTTP <code>GET</code> method.
     * 
     * @see #processRequest
     */
    @Override
    public void doGet( HttpServletRequest request, HttpServletResponse response )
    {
        processRequest( request, response );
    }

    /**
     * Handles the HTTP <code>HEAD</code> method.
     * 
     * @see #processRequest
     */
    @Override
    public void doHead( HttpServletRequest request, HttpServletResponse response )
    {
        processRequest( request, response );
    }

    /**
     * Handles the HTTP <code>OPTIONS</code> method.
     * 
     * @see #processRequest
     */
    @Override
    public void doOptions( HttpServletRequest request, HttpServletResponse response )
    {
        processRequest( request, response );
    }

    /**
     * Handles the HTTP <code>POST</code> method.
     * 
     * @see #processRequest
     */
    @Override
    public void doPost( HttpServletRequest request, HttpServletResponse response )
    {
        processRequest( request, response );
    }

    /**
     * Handles the HTTP <code>PUT</code> method.
     * 
     * @see #processRequest
     */
    @Override
    public void doPut( HttpServletRequest request, HttpServletResponse response )
    {
        processRequest( request, response );
    }

    /**
     * Handles the HTTP <code>TRACE</code> method.
     * 
     * @see #processRequest
     */
    @Override
    public void doTrace( HttpServletRequest request, HttpServletResponse response )
    {
        processRequest( request, response );
    }

    /**
     * Searches for HttpSession wrapper and calls processRequest on it.
     */
    public void processRequest( HttpServletRequest request, HttpServletResponse response )
    {
        try
        {
            PepServletRequest req = convertRequest( request );
            PepResponse res = convertResponse( response );

            System.out.println( "processRequest " + this );
            System.out.println( req );
            System.out.println( response );

            server.processRequest( req, res );
        }
        catch ( Exception ex )
        {
            ex.printStackTrace();
        }
    }

    /**
     * Searches for HttpSession wrapper and calls processRequest on it.
     */
    public void processRequestOLD( HttpServletRequest request, HttpServletResponse response )
    {
        PepSession pepSession = null;

        try
        {
            //
            // Lookup existing or create new PepSession.
            //
            pepSession = getPepSession( request.getSession() );

            //
            // Record the HttpSession's Thread.
            //
            if ( threadMap == null )
            {
                threadMap = new WeakHashMap<Thread, HttpSession>();
            }
            threadMap.put( Thread.currentThread(), request.getSession() );

            //
            // Do actual work.
            //
            pepSession.processRequest( request, response );
        }
        catch ( Exception ex )
        {
            if ( ex.getClass().getName().contains( "ClientAbortException" ) )
            {
                // If a client abort exception, then just ignore. The reason the class is not tested
                // here instead is because that it is a web server specific exception.
            }
            else
            {
                try
                {
                    //                    pepSession.setLastException( ex );
                    //                    pepSession.getErrorResponder().processRequest( pepSession, PepMethod.GET, request, response );
                    ex.printStackTrace();
                }
                catch ( Exception ex2 )
                {
                    ex.printStackTrace();
                    throw new RuntimeException( ex2 );
                }
            }
        }
        finally
        {
            //
            // Remove the association between the Thread and the HttpSession. It was temporary.
            //
            threadMap.remove( Thread.currentThread() );
        }
    }

    public PepServletRequest convertRequest( HttpServletRequest req )
    {
        System.out.println( "getAuthType= " + req.getAuthType() );
        System.out.println( "getCharacterEncoding= " + req.getCharacterEncoding() );
        System.out.println( "getContentLength= " + req.getContentLength() );
        System.out.println( "getContentType= " + req.getContentType() );
        System.out.println( "getContextPath= " + req.getContextPath() );
        System.out.println( "getLocalAddr= " + req.getLocalAddr() );
        System.out.println( "getLocalName= " + req.getLocalName() );
        System.out.println( "getLocalPort= " + req.getLocalPort() );
        System.out.println( "getMethod= " + req.getMethod() );
        System.out.println( "getPathInfo= " + req.getPathInfo() );
        System.out.println( "getPathTranslated= " + req.getPathTranslated() );
        System.out.println( "getProtocol= " + req.getProtocol() );
        System.out.println( "getQueryString= " + req.getQueryString() );
        System.out.println( "getRemoteAddr= " + req.getRemoteAddr() );
        System.out.println( "getRemoteHost= " + req.getRemoteHost() );
        System.out.println( "getRemotePort= " + req.getRemotePort() );
        System.out.println( "getRemoteUser= " + req.getRemoteUser() );
        System.out.println( "getRequestedSessionId= " + req.getRequestedSessionId() );
        System.out.println( "getRequestURI= " + req.getRequestURI() );
        System.out.println( "getRequestURL= " + req.getRequestURL() );
        System.out.println( "getScheme= " + req.getScheme() );
        System.out.println( "getServerName= " + req.getServerName() );
        System.out.println( "getServerPort= " + req.getServerPort() );
        System.out.println( "getServletPath= " + req.getServletPath() );
        System.out.println( "getUserPrincipal= " + req.getUserPrincipal() );
        System.out.println();

        PepServletRequest out = new PepServletRequest();
        out.setProtocol( req.getProtocol() );
        out.setMethod( PepMethod.valueOf( req.getMethod().toUpperCase() ) );
        out.setURL( req.getRequestURL().toString() );
        out.setQueryString( req.getQueryString() );

        return out;
    }

    public PepResponse convertResponse( HttpServletResponse res )
    {
        return new PepResponse( res );
    }
}
